﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Ferpect.ComponentModel
{
    [DefaultEvent("Start")]
    [Description("Raises button press events to identify the user's game pad. Useful in initializing the IInputService.")]
    public class BasicInputComponent : ControllerComponent
    {
        private GamePadState[] previousStates;
        private KeyboardState previousKeys;
        private IInputService inputService;
        private bool activating;

        [Category("Button Press")]
        public event EventHandler<InputEventArgs> Back;
        [Category("Button Press")]
        public event EventHandler<InputEventArgs> Start;
        [Category("Button Press")]
        public event EventHandler<InputEventArgs> A;
        [Category("Button Press")]
        public event EventHandler<InputEventArgs> B;
        [Category("Key Press")]
        public event EventHandler<InputEventArgs> Enter;
        [Category("Key Press")]
        public event EventHandler<InputEventArgs> Escape;

        public BasicInputComponent()
        {
            this.previousStates = new GamePadState[4];
        }

        /// <summary>
        /// Enables the component. The activating flag is set so that the Update method
        /// does not raise events on the next call. This prevents input events from
        /// being fired for button presses that occurred before this component was enabled.
        /// </summary>
        public override bool Enabled
        {
            set
            {
                this.activating = value;
                base.Enabled = value;
            }
        }

        public IInputService GetInputService()
        {
            if (this.inputService == null)
            {
                this.inputService = (IInputService)this.GetService(typeof(IInputService));
            }
            return this.inputService;
        }

        public override void Update(Microsoft.Xna.Framework.GameTime gameTime)
        {
            this.RaiseEvents(PlayerIndex.One);
            this.RaiseEvents(PlayerIndex.Two);
            this.RaiseEvents(PlayerIndex.Three);
            this.RaiseEvents(PlayerIndex.Four);
            this.RaiseKeyboardEvents();

            if (this.activating)
            {
                this.activating = false;
            }
        }

        private void RaiseEvents(PlayerIndex playerIndex)
        {
            GamePadState current = GamePad.GetState(playerIndex);
            if (current.IsConnected && !this.activating)
            {
                InputEventArgs args = InputEventArgs.GetInputEventArgs(playerIndex);
                if (this.Back != null && IsNewButtonPress(ref current, ref this.previousStates[(int)playerIndex], Buttons.Back))
                {
                    this.Back(this, args);
                }
                if (this.Start != null && IsNewButtonPress(ref current, ref this.previousStates[(int)playerIndex], Buttons.Start))
                {
                    this.Start(this, args);
                }
                if (this.A != null && IsNewButtonPress(ref current, ref this.previousStates[(int)playerIndex], Buttons.A))
                {
                    this.A(this, args);
                }
                if (this.B != null && IsNewButtonPress(ref current, ref this.previousStates[(int)playerIndex], Buttons.B))
                {
                    this.B(this, args);
                }
            }
            this.previousStates[(int)playerIndex] = current;
        }

        private void RaiseKeyboardEvents()
        {
            KeyboardState currentKeys = Keyboard.GetState();

            if (!this.activating)
            {
                if (this.Enter != null && currentKeys.IsKeyDown(Keys.Enter) && !this.previousKeys.IsKeyDown(Keys.Enter))
                {
                    this.Enter(this, InputEventArgs.Keyboard);
                }
                if (this.Escape != null && currentKeys.IsKeyDown(Keys.Escape) && !this.previousKeys.IsKeyDown(Keys.Escape))
                {
                    this.Escape(this, InputEventArgs.Keyboard);
                }
            }
            this.previousKeys = currentKeys;
        }

        private static bool IsNewButtonPress(ref GamePadState current, ref GamePadState previous, Buttons button)
        {
            return current.IsButtonDown(button) && (!previous.IsConnected || !previous.IsButtonDown(button));
        }

        private static bool IsNewKeyPress(ref KeyboardState current, ref KeyboardState previous, Keys button)
        {
            return current.IsKeyDown(button) && !previous.IsKeyDown(button);
        }
    }
}
